Ideas and exercises come from https://r4ds.had.co.nz/transform.html

Additional notes by TCS

Setup

First, we load the tidyverse package and a dataset. This data frame contains all 336,776 flights that departed from New York City in 2013.

The Lahman baseball dataset is also used.

require(nycflights13)
require(tidyverse)
require(hms)
require(Lahman)
flights
NA

Review: Basic dplyr functions

There are 5 key functions (“verbs”), plus a helper function, that do most data manipulation tasks in dplyr:

filter() gives you a subset of rows based on values

NA handling in filter

filter() includes ONLY rows where the condition is TRUE; it excludes both FALSE and NA values. If you want to preserve missing values, ask for them explicitly:

df <- tibble(x = c(1, NA, 3))
(biggerthan1 <- filter(df, x > 1))
(bigorNA <- filter(df, is.na(x) | x > 1))

arrange() is for sorting rows

select() gives you a subset of columns by name

You can name each column, or specify a range using a colon.
As with other R selections, you can omit certain columns using the minus sign.
You can add multiple arguments to include more columns in the selection.

Partial names

select() does not have to use exact column matches.

  • starts_with("foo")
  • ends_with("bar")
  • contains("foobar")
  • matches(some_regex)
  • num_range("x", 1:3) matches x1, x2 and x3

Variants on select()

  • rename() is considered a variant of select() where you take a column, change its name, and keep all other columns as well.
  • everything() is a helper for select() that lets you move one or a few columns to the beginning (left) of the table, while retaining all other columns.

mutate() is for creating new variables from old

(flights_sml <- select(flights, 
  year:day, 
  ends_with("delay"), 
  distance, 
  air_time
))
(flights_less_sml <- mutate(flights_sml,
  gain = dep_delay - arr_delay,
  speed = distance / air_time * 60
))
(flights_even_more <- mutate(flights_sml,
  gain = dep_delay - arr_delay,
  hours = air_time / 60,
  gain_per_hour = gain / hours
))
(flights_new <- transmute(flights,
  gain = dep_delay - arr_delay,
  hours = air_time / 60,
  gain_per_hour = gain / hours
))

Many vectorized functions work with mutate()

Some examples:
* Arithmetic, logs (vectors recycle to match length)
* Modulus arithmetic: %/% (integer division) and %% (remainder), where x == y * (x %/% y) + (x %% y)
* Surrounding values: lead(), lag()
* Cumulative values such as cumsum() and cumprod()
* With the Rcpproll package, rolling sums, etc.
* Ranking such as min_rank()

Exercises for mutate()

  1. Currently dep_time and sched_dep_time are convenient to look at, but hard to compute with because they’re not really continuous numbers. Convert them to a more convenient representation of number of minutes since midnight.

  2. Compare air_time with arr_time - dep_time. What do you expect to see? What do you see? What do you need to do to fix it?

*The times are in HHMM format, so straight subtraction does not work. We can convert to time objects, or use modulus arithmetic to convert to minutes and back to time.

  1. Compare dep_time, sched_dep_time, and dep_delay. How would you expect those three numbers to be related?

We expect dep_delay = sched_dep_time - dep_time. However, for > 60 min difference, we have the same time issue, and solution, as in exercise 2.

  1. Find the 10 most delayed flights using a ranking function. How do you want to handle ties? Carefully read the documentation for min_rank().
  • rank() goes smallest to largest, gives decimals (averages) for ties, 1, 2.5, 2.5, 4,… and puts NAs last by default, but can change behavior using na.last. rank(desc()) goes largest to smallest.
  • rank() has different methods of handling ties, producing all-integer output. First; last; random; max (max rank of all ties so there might not be a #1); min (min rank of all tied elements “as used in sports,” so you can “tie for 1st” – (1,1,3,4…)) – this is min_rank(). first, last, random produce all-unique ranks.
  • If you preserve or average ties, you could end up with more or fewer than 10 in the result. Therefore to get exactly 10 we will break ties randomly.
  • If you want to use another column to break ties, then I don’t see a way within rank() functions. You could use arrange() and then row numbers.
  1. What does 1:3 + 1:10 return? Why?

The 1:3 vector is recycled, so you get 4+1, 5+2, etc.

  1. What trigonometric functions does R provide?

The usual functions, plus a few others. Angles are in radians. cospi(x) and kin give the functions of pi times x, only for x = multiples of 0.5

# The time is in HMM or HHMM format. Therefore to get the hours we divide by 100, and for minutes we take the remainder. (Did not deal with the times = 2400!)

(better_time <- mutate(flights, dep_min_since_midnight = (dep_time %/% 100) * 60 + dep_time %% 100, sched_dep_min_since_midnight = (sched_dep_time %/% 100) * 60 + sched_dep_time %% 100))

(compare <- select(flights, air_time, arr_time, dep_time) %>% mutate(spent_time = arr_time-dep_time))

# the times are in HHMM format, so straight subtraction does not work

(realtimes <- mutate(flights, 
                    arr_time_hms = hms(NULL, arr_time %% 100, arr_time %/% 100),
                    dep_time_hms = hms(NULL, dep_time %% 100, dep_time %/% 100),
                    spent_time_hms = difftime(arr_time_hms, dep_time_hms, units = "mins")))
  
# mutate to add the ranking function (alternatively you could probably arrange and then take by row number)

# if you preserve or average ties, you could end up with more or fewer than 10 in the result. Therefore to get exactly 10 we will break ties randomly.

(arr_ranked <- mutate(flights, arr_delay_rank = rank(desc(arr_delay), ties.method = "random")))
(top_10_delay <- arr_ranked %>%  filter(arr_delay_rank <= 10))

summarise() collapses groups into single values

*When using summarise() you usually also want groups created by group_by(). (If you didn’t have a group, you could just call the desired functions on whole columns.)

*You can group by multiple levels

*If you need to remove a grouping use ungroup()

*Using the pipe we can quickly feed groups into summaries and do other useful tasks efficiently.

*Among tidyverse functions, only ggplot2 doesn’t work as well with pipes: “it was written before the pipe was discovered. Unfortunately, the next iteration of ggplot2, ggvis, which does use the pipe, isn’t quite ready for prime time yet.” But it is used in some examples below.


# with pipe
(delays <- flights %>% 
  group_by(dest) %>% 
  summarise(
    count = n(),
    dist = mean(distance, na.rm = TRUE),
    delay = mean(arr_delay, na.rm = TRUE)
  ) %>% 
  filter(count > 20, dest != "HNL"))

*Beware – if you have any missing values (NAs) in your data your summary value will also be NA, unless you set na.rm = TRUE.

Other functions for summarising [sic]

  • Count, Sum, Median, etc.
    ** Other types of count: n(), n_distinct(x), sum(!is.na(x)); count(x, sort = TRUE) puts largest groups at top
  • Measures of spread: sd(x), IQR(x), mad(x). The interquartile range IQR(x) and median absolute deviation mad(x) are robust equivalents that may be more useful if you have outliers.
  • Summaries of logical subsets such as mean(arr_delay[arr_delay > 0]) = the mean of all those values > 0
  • Rankings: min(x), quantile(x, 0.25) [25th percentile value], max(x)
  • Positions: first(x), nth(x, 2), last(x). These work similarly to x[1], x[2], and x[length(x)] but let you set a default value if that position does not exist
# plot delay vs distance
(p <- ggplot(data = delays, mapping = aes(x = dist, y = delay)) +
   geom_point(aes(size = count), alpha = 1/3) +
   geom_smooth(se = FALSE)
)

# remove NAs when computing the summary
(delayed <- flights %>% 
  group_by(year, month, day) %>% 
  summarise(mean = mean(dep_delay, na.rm = TRUE)))
`summarise()` has grouped output by 'year', 'month'. You can override using the `.groups` argument.
# remove NAs by removing data at the beginning
(not_cancelled <- flights %>% 
  filter(!is.na(dep_delay), !is.na(arr_delay))
)
(true_delayed <- not_cancelled %>% 
  group_by(year, month, day) %>% 
  summarise(mean = mean(dep_delay, na.rm = TRUE)))
`summarise()` has grouped output by 'year', 'month'. You can override using the `.groups` argument.
# which planes have the greatest delays?
(delays <- not_cancelled %>% 
  group_by(tailnum) %>% 
  summarise(
    delay = mean(arr_delay)
  ))
# histogram of average delay times
(p1 <- ggplot(data = delays, mapping = aes(x = delay)) + 
  geom_freqpoly(binwidth = 10))


# remove NAs and cancelled
(delays <- not_cancelled %>% 
  group_by(tailnum) %>% 
  summarise(
    delay = mean(arr_delay, na.rm = TRUE),
    n = n()
  ))
# scatterplot of average delay vs n (rotated histogram actually, but you can see more points)
# shows that the high averages are almost all based on few data points
(p3 <- ggplot(data = delays, mapping = aes(x = n, y = delay)) + 
  geom_point(alpha = 1/10))


# filter out the points with low n before plotting
(p4 <- delays %>% 
  filter(n > 25) %>% 
  ggplot(mapping = aes(x = n, y = delay)) + 
    geom_point(alpha = 1/10))


# instead of averaging early and late flights together, start with the logical subset of the positive (late) delays
(not_cancelled %>% 
  group_by(year, month, day) %>% 
  summarise(
    avg_delay1 = mean(arr_delay),
    avg_delay2 = mean(arr_delay[arr_delay > 0]) # the average positive delay
  ))
`summarise()` has grouped output by 'year', 'month'. You can override using the `.groups` argument.
# looking at standard deviation of distance to each destination
(spread_of_origins <- not_cancelled %>% 
  group_by(dest) %>% 
  summarise(distance_sd = sd(distance)) %>% 
  arrange(desc(distance_sd)))

# When do the first and last flights leave each day?
(first_and_last <- not_cancelled %>% 
  group_by(year, month, day) %>% 
  summarise(
    first = min(dep_time),
    last = max(dep_time)
  ))
`summarise()` has grouped output by 'year', 'month'. You can override using the `.groups` argument.
(first_last_2 <- not_cancelled %>% 
  group_by(year, month, day) %>% 
  summarise(
    first_dep = first(dep_time), # This works ONLY because the table is already ordered by dep_time!
    last_dep = last(dep_time)
  ))
`summarise()` has grouped output by 'year', 'month'. You can override using the `.groups` argument.
# another way to find the 1st and last per day
(rankfilt <- not_cancelled %>% 
  group_by(year, month, day) %>% 
  mutate(r = min_rank(desc(dep_time))) %>% 
  filter(r %in% range(r))) # range gives you the min and max values

You can group hierarchically and see summaries within summaries. You can ungroup if needed.

# group multiple levels

daily <- group_by(flights, year, month, day)
(per_day   <- summarise(daily, flights = n()))
`summarise()` has grouped output by 'year', 'month'. You can override using the `.groups` argument.
# ungroup

(daily %>% 
  ungroup() %>%             # no longer grouped by date
  summarise(flights = n()))  # all flights
NA

Here we continue digressing (IMO) on the relationship between variation and sample size, using examples from the Lahman baseball statistics package.

# Convert to a tibble so it prints nicely
(batting <- as_tibble(Lahman::Batting))

# batting average vs at bats per player


# visualize avg with the number of at-bats 
(batters <- batting %>% 
  group_by(playerID) %>% 
  summarise(
    ba = sum(H, na.rm = TRUE) / sum(AB, na.rm = TRUE),
    ab = sum(AB, na.rm = TRUE)
  ))

(p <- batters %>% 
  filter(ab > 100) %>% 
  ggplot(mapping = aes(x = ab, y = ba)) +
    geom_point() + 
    geom_smooth(se = FALSE))


# who are the "best" batters? The lucky ones? Note the at-bats for the top of the list
(high_avg <- batters %>% 
  arrange(desc(ba))
)

Now we get back to the flight data and try methods of counting.

  • Count the carriers going to each destination
  • Count valid and invalid values using is.na
  • Count values within certain ranges
  • We can weight our counts by another column, e.g. which aircraft have the most flight miles.
  • BUT be careful when summarising by multiple levels. The mean of 2 different-sized groups is NOT the mean of the 2 means. Then, weighting is useful.
# Which destinations have the most carriers?
(not_cancelled %>% 
  group_by(dest) %>% 
  summarise(carriers = n_distinct(carrier)) %>% # unique values
  arrange(desc(carriers)))

# how many values are entered?
(valid_arr_time <- sum(!is.na(not_cancelled$arr_time)))
[1] 327346
# or to get the fraction of valid values
(valid_arr_time_frac <- mean(!is.na(not_cancelled$arr_time)))
[1] 1
(valid_arr_time_frac2 <- mean(!is.na(flights$arr_time)))
[1] 0.9741282
# count() is a dplyr summary of a grouped table

(flights_per_dest <- not_cancelled %>% 
  count(dest))

# you can weight counts by another variable

(aircraft_miles <- not_cancelled %>% 
  count(tailnum, wt = distance))
NA
NA

Exercises for counting and summarising

  1. Brainstorm at least 5 different ways to assess the typical delay characteristics of a group of flights. Consider the following scenarios:
  • A flight is 15 minutes early 50% of the time, and 15 minutes late 50% of the time.
  • A flight is always 10 minutes late.
  • A flight is 30 minutes early 50% of the time, and 30 minutes late 50% of the time.
  • 99% of the time a flight is on time. 1% of the time it’s 2 hours late.

To clarify these differences, use % of flights that are delayed; mean and standard deviation of delays including pos and neg; mean absolute value of delays > 0; median delay

Which is more important: arrival delay or departure delay?

To the passenger, arrival delay is most important. To the airport, departure delay is also important because other flights are affected.

  1. Come up with another approach that will give you the same output as not_cancelled %>% count(dest) and not_cancelled %>% count(tailnum, wt = distance) (without using count()).
  • count the flights per destination without count(): use n()
  • get aircraft miles: group by the tailnumber and sum the miles
  1. Our definition of cancelled flights ( (is.na(dep_delay) | is.na(arr_delay) ) is slightly suboptimal. Why? Which is the most important column?
  • One of these columns should be sufficient to check. The arrival might be the most important since occasionally a flight could “depart” the gate then return from the tarmac. Or there could be missing data. More stringently you could check for the intersection (&)
  1. Look at the number of cancelled flights per day. Is there a pattern? Is the proportion of cancelled flights related to the average delay?
  • groupby year month day, summarize num cancelled and average delay, graph
  1. Which carrier has the worst delays? Challenge: can you disentangle the effects of bad airports vs. bad carriers? Why/why not? (Hint: think about flights %>% group_by(carrier, dest) %>% summarise(n()))

Group by carrier AND airport, look at % on-time flights, mean and median delay (>0 and total). Compare carrier in an airport to total for that airport, or (in case one predominates, check % flights from that carrier) the total for other carriers at the same airport.

  1. What does the sort argument to count() do? When might you use it?

It displays the largest groups at the top of the output. You could use it to do a quickie summary instead of 2 separate commands to find, e.g., the most delayed flights or whatever.

# ---- assess lateness in different ways
# distinguish early + late vs constantly late vs. nearly always on time; 
#mean and standard deviation of delays; mean absolute value of delays; median delay


# count the flights per destination without count()
(flights %>% group_by(dest) %>% summarise(n())) 

# get aircraft miles: group by the tailnumber and sum the miles  

Combining functions: Grouped mutates and filters

#Find the worst members of each group:

flights_sml %>% 
  group_by(year, month, day) %>%
  filter(rank(desc(arr_delay)) < 10)

# Find all groups bigger than a threshold:

popular_dests <- flights %>% 
  group_by(dest) %>% 
  filter(n() > 365)

# Standardise to compute per group metrics:

popular_dests %>% 
  filter(arr_delay > 0) %>% 
  mutate(prop_delay = arr_delay / sum(arr_delay)) %>% 
  select(year:day, dest, arr_delay, prop_delay)

Exercises for grouped mutates

  1. Refer back to the lists of useful mutate and filtering functions. Describe how each operation changes when you combine it with grouping.

  2. Which plane (tailnum) has the worst on-time record?

check % on-time check % on-time within some interval

  1. What time of day should you fly if you want to avoid delays as much as possible?

divide day into bins and summarise, or plot time vs % on-time

  1. For each destination, compute the total minutes of delay. For each flight, compute the proportion of the total delay for its destination.

group by dest, sum delays (positive). 2nd question is not very clear – but i think they want (delay for a single flight)/(total delays for the destination). or (delay for all flights with that number)/(total delays for the destination).

  1. Delays are typically temporally correlated: even once the problem that caused the initial delay has been resolved, later flights are delayed to allow earlier flights to leave. Using lag(), explore how the delay of a flight is related to the delay of the immediately preceding flight.

sort by time, graph delay vs delay(lag)

  1. Look at each destination. Can you find flights that are suspiciously fast? (i.e. flights that represent a potential data entry error). Compute the air time of a flight relative to the shortest flight to that destination. Which flights were most delayed in the air?

  2. Find all destinations that are flown by at least two carriers. Use that information to rank the carriers.

most ontime, lowest average delay magnitude

  1. For each plane, count the number of flights before the first delay of greater than 1 hour.

group by tailnumber, sort by date(?), find earliest 1 hr delay, count flights by that plane with date/time < that value

LS0tCnRpdGxlOiAiTm90ZXMgb24gUiBmb3IgRGF0YSBTY2llbmNlIENoYXB0ZXIgNTogRGF0YSB0cmFuc2Zvcm1hdGlvbiwgUGFydCAyIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgoqSWRlYXMgYW5kIGV4ZXJjaXNlcyBjb21lIGZyb20gaHR0cHM6Ly9yNGRzLmhhZC5jby5uei90cmFuc2Zvcm0uaHRtbCoKCipBZGRpdGlvbmFsIG5vdGVzIGJ5IFRDUyoKCiMgU2V0dXAKRmlyc3QsIHdlIGxvYWQgdGhlIGB0aWR5dmVyc2VgIHBhY2thZ2UgYW5kIGEgZGF0YXNldC4gVGhpcyBkYXRhIGZyYW1lIGNvbnRhaW5zIGFsbCAzMzYsNzc2IGZsaWdodHMgdGhhdCBkZXBhcnRlZCBmcm9tIE5ldyBZb3JrIENpdHkgaW4gMjAxMy4KClRoZSBMYWhtYW4gYmFzZWJhbGwgZGF0YXNldCBpcyBhbHNvIHVzZWQuCgpgYGB7ciBzZXR1cH0KcmVxdWlyZShueWNmbGlnaHRzMTMpCnJlcXVpcmUodGlkeXZlcnNlKQpyZXF1aXJlKGhtcykKcmVxdWlyZShMYWhtYW4pCmZsaWdodHMKCmBgYAoKIyBSZXZpZXc6IEJhc2ljIGRwbHlyIGZ1bmN0aW9ucwoKVGhlcmUgYXJlIDUga2V5IGZ1bmN0aW9ucyAoInZlcmJzIiksIHBsdXMgYSBoZWxwZXIgZnVuY3Rpb24sIHRoYXQgZG8gbW9zdCBkYXRhIG1hbmlwdWxhdGlvbiB0YXNrcyBpbiBkcGx5cjogIAoKKiBgZmlsdGVyYDogcGljayBvYnNlcnZhdGlvbnMgYnkgdmFsdWVzCiogYGFycmFuZ2VgOiByZW9yZGVyIHJvd3MgIAoqIGBzZWxlY3RgOiBwaWNrIHZhcmlhYmxlcyBieSBuYW1lICAKKiBgbXV0YXRlYDogY3JlYXRlIG5ldyB2YXJpYWJsZXMgZnJvbSBleGlzdGluZyBvbmVzLCB1c2luZyBmdW5jdGlvbnMgIAoqIGBzdW1tYXJpc2VgOiBjb2xsYXBzZSB2YWx1ZXMgaW50byBzaW5nbGUgb25lcyAgCiogYGdyb3VwX2J5YDogY2hhbmdlIHNjb3BlIG9mIGEgdmVyYiBmcm9tIHRoZSB3aG9sZSBkYXRhc2V0IHRvIGluZGl2aWR1YWwgZ3JvdXBzIAoKIyMgYGZpbHRlcigpYCBnaXZlcyB5b3UgYSBzdWJzZXQgb2Ygcm93cyBiYXNlZCBvbiB2YWx1ZXMKCiMjIE5BIGhhbmRsaW5nIGluIGBmaWx0ZXJgCgpgZmlsdGVyKClgIGluY2x1ZGVzIE9OTFkgcm93cyB3aGVyZSB0aGUgY29uZGl0aW9uIGlzIFRSVUU7IGl0IGV4Y2x1ZGVzIGJvdGggRkFMU0UgYW5kIE5BIHZhbHVlcy4gSWYgeW91IHdhbnQgdG8gcHJlc2VydmUgbWlzc2luZyB2YWx1ZXMsIGFzayBmb3IgdGhlbSBleHBsaWNpdGx5OiAgCgpgYGB7ciBmaWx0ZXIgTkEgZXhhbXBsZXMsIGVjaG8gPSBUUlVFfQpkZiA8LSB0aWJibGUoeCA9IGMoMSwgTkEsIDMpKQooYmlnZ2VydGhhbjEgPC0gZmlsdGVyKGRmLCB4ID4gMSkpCihiaWdvck5BIDwtIGZpbHRlcihkZiwgaXMubmEoeCkgfCB4ID4gMSkpCmBgYAojIGBhcnJhbmdlKClgIGlzIGZvciBzb3J0aW5nIHJvd3MKCiogVGhlIGFyZ3VtZW50cyBmb3IgYGFycmFuZ2UoKWAgYXJlIGEgZGF0YWZyYW1lIGFuZCBjb2x1bW4gbmFtZShzKS4gICAKKiBUaGUgYGRlc2NgIG9wdGlvbiByZXZlcnNlcyB0aGUgb3JkZXIuICAKKiBNaXNzaW5nIHZhbHVlcyAoTkFzKSBhcmUgYWx3YXlzIGF0IHRoZSBlbmQgcmVnYXJkbGVzcyBvZiBgZGVzYygpYCAKIAojIGBzZWxlY3QoKWAgZ2l2ZXMgeW91IGEgc3Vic2V0IG9mIGNvbHVtbnMgYnkgbmFtZQoKWW91IGNhbiBuYW1lIGVhY2ggY29sdW1uLCBvciBzcGVjaWZ5IGEgcmFuZ2UgdXNpbmcgYSBjb2xvbi4gIApBcyB3aXRoIG90aGVyIFIgc2VsZWN0aW9ucywgeW91IGNhbiBvbWl0IGNlcnRhaW4gY29sdW1ucyB1c2luZyB0aGUgbWludXMgc2lnbi4gIApZb3UgY2FuIGFkZCBtdWx0aXBsZSBhcmd1bWVudHMgdG8gaW5jbHVkZSBtb3JlIGNvbHVtbnMgaW4gdGhlIHNlbGVjdGlvbi4gIAoKIyMgUGFydGlhbCBuYW1lcyAgCgpgc2VsZWN0KClgIGRvZXMgbm90IGhhdmUgdG8gdXNlIGV4YWN0IGNvbHVtbiBtYXRjaGVzLiAgCgoqIGBzdGFydHNfd2l0aCgiZm9vIilgICAKKiBgZW5kc193aXRoKCJiYXIiKWAgIAoqIGBjb250YWlucygiZm9vYmFyIilgICAKKiBgbWF0Y2hlcyhzb21lX3JlZ2V4KWAgIAoqIGBudW1fcmFuZ2UoIngiLCAxOjMpYCBtYXRjaGVzIHgxLCB4MiBhbmQgeDMgIAoKIyMgVmFyaWFudHMgb24gYHNlbGVjdCgpYAoKKiBgcmVuYW1lKClgIGlzIGNvbnNpZGVyZWQgYSB2YXJpYW50IG9mIHNlbGVjdCgpIHdoZXJlIHlvdSB0YWtlIGEgY29sdW1uLCBjaGFuZ2UgaXRzIG5hbWUsIGFuZCBrZWVwIGFsbCBvdGhlciBjb2x1bW5zIGFzIHdlbGwuCiogYGV2ZXJ5dGhpbmcoKWAgaXMgYSBoZWxwZXIgZm9yIHNlbGVjdCgpIHRoYXQgbGV0cyB5b3UgbW92ZSBvbmUgb3IgYSBmZXcgY29sdW1ucyB0byB0aGUgYmVnaW5uaW5nIChsZWZ0KSBvZiB0aGUgdGFibGUsIHdoaWxlIHJldGFpbmluZyBhbGwgb3RoZXIgY29sdW1ucy4gIAoKIyBgbXV0YXRlKClgIGlzIGZvciBjcmVhdGluZyBuZXcgdmFyaWFibGVzIGZyb20gb2xkICAKCiogVGhlIG5ldyBjb2x1bW5zIGFyZSBhZGRlZCBhdCB0aGUgZW5kIChyaWdodCkgb2YgdGhlIGRhdGEgZnJhbWUuICAKKiBZb3UgY2FuIGFkZCBtdWx0aXBsZSBjb2x1bW5zIGluIG9uZSBjYWxsLCBhbmQgdGhlIGxhdGVyIGNvbHVtbnMgY2FuIHVzZSB2YXJpYWJsZXMgZGVmaW5lZCBlYXJsaWVyIGluIHRoZSBjYWxsICAKKiBgdHJhbnNtdXRlKClgIGlzIGEgdmFyaWFudCB3aGVyZSB5b3Uga2VlcCBPTkxZIHRoZSBuZXdseSBkZWZpbmVkIGNvbHVtbnMgIAoKYGBge3IgbXV0YXRlIGV4YW1wbGVzLCBlY2hvID0gVFJVRX0KKGZsaWdodHNfc21sIDwtIHNlbGVjdChmbGlnaHRzLCAKICB5ZWFyOmRheSwgCiAgZW5kc193aXRoKCJkZWxheSIpLCAKICBkaXN0YW5jZSwgCiAgYWlyX3RpbWUKKSkKKGZsaWdodHNfbGVzc19zbWwgPC0gbXV0YXRlKGZsaWdodHNfc21sLAogIGdhaW4gPSBkZXBfZGVsYXkgLSBhcnJfZGVsYXksCiAgc3BlZWQgPSBkaXN0YW5jZSAvIGFpcl90aW1lICogNjAKKSkKKGZsaWdodHNfZXZlbl9tb3JlIDwtIG11dGF0ZShmbGlnaHRzX3NtbCwKICBnYWluID0gZGVwX2RlbGF5IC0gYXJyX2RlbGF5LAogIGhvdXJzID0gYWlyX3RpbWUgLyA2MCwKICBnYWluX3Blcl9ob3VyID0gZ2FpbiAvIGhvdXJzCikpCihmbGlnaHRzX25ldyA8LSB0cmFuc211dGUoZmxpZ2h0cywKICBnYWluID0gZGVwX2RlbGF5IC0gYXJyX2RlbGF5LAogIGhvdXJzID0gYWlyX3RpbWUgLyA2MCwKICBnYWluX3Blcl9ob3VyID0gZ2FpbiAvIGhvdXJzCikpCmBgYAoKIyMgTWFueSB2ZWN0b3JpemVkIGZ1bmN0aW9ucyB3b3JrIHdpdGggYG11dGF0ZSgpYCAgCgpTb21lIGV4YW1wbGVzOiAgCiogQXJpdGhtZXRpYywgbG9ncyAodmVjdG9ycyByZWN5Y2xlIHRvIG1hdGNoIGxlbmd0aCkgIAoqIE1vZHVsdXMgYXJpdGhtZXRpYzogICUvJSAoaW50ZWdlciBkaXZpc2lvbikgYW5kICUlICAgKHJlbWFpbmRlciksIHdoZXJlIHggPT0geSAqICh4ICUvJSB5KSArICh4ICUlIHkpICAKKiBTdXJyb3VuZGluZyB2YWx1ZXM6IGxlYWQoKSwgbGFnKCkgIAoqIEN1bXVsYXRpdmUgdmFsdWVzIHN1Y2ggYXMgY3Vtc3VtKCkgYW5kIGN1bXByb2QoKSAgCiogV2l0aCB0aGUgUmNwcHJvbGwgcGFja2FnZSwgcm9sbGluZyBzdW1zLCBldGMuICAKKiBSYW5raW5nIHN1Y2ggYXMgbWluX3JhbmsoKSAgCgojIyBFeGVyY2lzZXMgZm9yIGBtdXRhdGUoKWAgIAoKMS4gQ3VycmVudGx5IGRlcF90aW1lIGFuZCBzY2hlZF9kZXBfdGltZSBhcmUgY29udmVuaWVudCB0byBsb29rIGF0LCBidXQgaGFyZCB0byBjb21wdXRlIHdpdGggYmVjYXVzZSB0aGV54oCZcmUgbm90IHJlYWxseSBjb250aW51b3VzIG51bWJlcnMuIENvbnZlcnQgdGhlbSB0byBhIG1vcmUgY29udmVuaWVudCByZXByZXNlbnRhdGlvbiBvZiBudW1iZXIgb2YgbWludXRlcyBzaW5jZSBtaWRuaWdodC4gIAoKMi4gQ29tcGFyZSBhaXJfdGltZSB3aXRoIGFycl90aW1lIC0gZGVwX3RpbWUuIFdoYXQgZG8geW91IGV4cGVjdCB0byBzZWU/IFdoYXQgZG8geW91IHNlZT8gV2hhdCBkbyB5b3UgbmVlZCB0byBkbyB0byBmaXggaXQ/ICAKCipUaGUgdGltZXMgYXJlIGluIEhITU0gZm9ybWF0LCBzbyBzdHJhaWdodCBzdWJ0cmFjdGlvbiBkb2VzIG5vdCB3b3JrLiBXZSBjYW4gY29udmVydCB0byB0aW1lIG9iamVjdHMsIG9yIHVzZSBtb2R1bHVzIGFyaXRobWV0aWMgdG8gY29udmVydCB0byBtaW51dGVzIGFuZCBiYWNrIHRvIHRpbWUuICAKCjMuIENvbXBhcmUgZGVwX3RpbWUsIHNjaGVkX2RlcF90aW1lLCBhbmQgZGVwX2RlbGF5LiBIb3cgd291bGQgeW91IGV4cGVjdCB0aG9zZSB0aHJlZSBudW1iZXJzIHRvIGJlIHJlbGF0ZWQ/ICAKCipXZSBleHBlY3QgZGVwX2RlbGF5ID0gc2NoZWRfZGVwX3RpbWUgLSBkZXBfdGltZS4gSG93ZXZlciwgZm9yID4gNjAgbWluIGRpZmZlcmVuY2UsIHdlIGhhdmUgdGhlIHNhbWUgdGltZSBpc3N1ZSwgYW5kIHNvbHV0aW9uLCBhcyBpbiBleGVyY2lzZSAyLioKCjQuIEZpbmQgdGhlIDEwIG1vc3QgZGVsYXllZCBmbGlnaHRzIHVzaW5nIGEgcmFua2luZyBmdW5jdGlvbi4gSG93IGRvIHlvdSB3YW50IHRvIGhhbmRsZSB0aWVzPyBDYXJlZnVsbHkgcmVhZCB0aGUgZG9jdW1lbnRhdGlvbiBmb3IgbWluX3JhbmsoKS4gIAoKKiAqYHJhbmsoKWAgZ29lcyBzbWFsbGVzdCB0byBsYXJnZXN0LCBnaXZlcyBkZWNpbWFscyAoYXZlcmFnZXMpIGZvciB0aWVzLCAxLCAyLjUsIDIuNSwgNCwuLi4gYW5kIHB1dHMgTkFzIGxhc3QgYnkgZGVmYXVsdCwgYnV0IGNhbiBjaGFuZ2UgYmVoYXZpb3IgdXNpbmcgYG5hLmxhc3RgLiAgYHJhbmsoZGVzYygpKWAgZ29lcyBsYXJnZXN0IHRvIHNtYWxsZXN0LiogIAoqICpgcmFuaygpYCBoYXMgZGlmZmVyZW50IG1ldGhvZHMgb2YgaGFuZGxpbmcgdGllcywgcHJvZHVjaW5nIGFsbC1pbnRlZ2VyIG91dHB1dC4gRmlyc3Q7IGxhc3Q7IHJhbmRvbTsgbWF4IChtYXggcmFuayBvZiBhbGwgdGllcyBzbyB0aGVyZSBtaWdodCBub3QgYmUgYSAjMSk7IG1pbiAobWluIHJhbmsgb2YgYWxsIHRpZWQgZWxlbWVudHMgImFzIHVzZWQgaW4gc3BvcnRzLCIgc28geW91IGNhbiAidGllIGZvciAxc3QiIC0tICgxLDEsMyw0Li4uKSkgLS0gdGhpcyBpcyBtaW5fcmFuaygpLiBgZmlyc3RgLCBgbGFzdGAsIGByYW5kb21gIHByb2R1Y2UgYWxsLXVuaXF1ZSByYW5rcy4qICAKKiAqSWYgeW91IHByZXNlcnZlIG9yIGF2ZXJhZ2UgdGllcywgeW91IGNvdWxkIGVuZCB1cCB3aXRoIG1vcmUgb3IgZmV3ZXIgdGhhbiAxMCBpbiB0aGUgcmVzdWx0LiBUaGVyZWZvcmUgdG8gZ2V0IGV4YWN0bHkgMTAgd2Ugd2lsbCBicmVhayB0aWVzIHJhbmRvbWx5LiogCiogKklmIHlvdSB3YW50IHRvIHVzZSBhbm90aGVyIGNvbHVtbiB0byBicmVhayB0aWVzLCB0aGVuIEkgZG9uJ3Qgc2VlIGEgd2F5IHdpdGhpbiBgcmFuaygpYCBmdW5jdGlvbnMuIFlvdSBjb3VsZCB1c2UgYGFycmFuZ2UoKWAgYW5kIHRoZW4gcm93IG51bWJlcnMuKiAgCgo1LiBXaGF0IGRvZXMgMTozICsgMToxMCByZXR1cm4/IFdoeT8gIAoKKlRoZSAxOjMgdmVjdG9yIGlzIHJlY3ljbGVkLCBzbyB5b3UgZ2V0IDQrMSwgNSsyLCBldGMuKiAgCgo2LiBXaGF0IHRyaWdvbm9tZXRyaWMgZnVuY3Rpb25zIGRvZXMgUiBwcm92aWRlPyAgCgoqVGhlIHVzdWFsIGZ1bmN0aW9ucywgcGx1cyBhIGZldyBvdGhlcnMuIEFuZ2xlcyBhcmUgaW4gcmFkaWFucy4gYGNvc3BpKHgpYCBhbmQga2luIGdpdmUgdGhlIGZ1bmN0aW9ucyBvZiBwaSB0aW1lcyB4LCBvbmx5IGZvciB4ID0gbXVsdGlwbGVzIG9mIDAuNSoKCmBgYHtyIG11dGF0ZSBleGVyY2lzZXMsIGVjaG8gPVRSVUV9CiMgVGhlIHRpbWUgaXMgaW4gSE1NIG9yIEhITU0gZm9ybWF0LiBUaGVyZWZvcmUgdG8gZ2V0IHRoZSBob3VycyB3ZSBkaXZpZGUgYnkgMTAwLCBhbmQgZm9yIG1pbnV0ZXMgd2UgdGFrZSB0aGUgcmVtYWluZGVyLiAoRGlkIG5vdCBkZWFsIHdpdGggdGhlIHRpbWVzID0gMjQwMCEpCgooYmV0dGVyX3RpbWUgPC0gbXV0YXRlKGZsaWdodHMsIGRlcF9taW5fc2luY2VfbWlkbmlnaHQgPSAoZGVwX3RpbWUgJS8lIDEwMCkgKiA2MCArIGRlcF90aW1lICUlIDEwMCwgc2NoZWRfZGVwX21pbl9zaW5jZV9taWRuaWdodCA9IChzY2hlZF9kZXBfdGltZSAlLyUgMTAwKSAqIDYwICsgc2NoZWRfZGVwX3RpbWUgJSUgMTAwKSkKCihjb21wYXJlIDwtIHNlbGVjdChmbGlnaHRzLCBhaXJfdGltZSwgYXJyX3RpbWUsIGRlcF90aW1lKSAlPiUgbXV0YXRlKHNwZW50X3RpbWUgPSBhcnJfdGltZS1kZXBfdGltZSkpCgojIHRoZSB0aW1lcyBhcmUgaW4gSEhNTSBmb3JtYXQsIHNvIHN0cmFpZ2h0IHN1YnRyYWN0aW9uIGRvZXMgbm90IHdvcmsKCihyZWFsdGltZXMgPC0gbXV0YXRlKGZsaWdodHMsIAogICAgICAgICAgICAgICAgICAgIGFycl90aW1lX2htcyA9IGhtcyhOVUxMLCBhcnJfdGltZSAlJSAxMDAsIGFycl90aW1lICUvJSAxMDApLAogICAgICAgICAgICAgICAgICAgIGRlcF90aW1lX2htcyA9IGhtcyhOVUxMLCBkZXBfdGltZSAlJSAxMDAsIGRlcF90aW1lICUvJSAxMDApLAogICAgICAgICAgICAgICAgICAgIHNwZW50X3RpbWVfaG1zID0gZGlmZnRpbWUoYXJyX3RpbWVfaG1zLCBkZXBfdGltZV9obXMsIHVuaXRzID0gIm1pbnMiKSkpCiAgCiMgbXV0YXRlIHRvIGFkZCB0aGUgcmFua2luZyBmdW5jdGlvbiAoYWx0ZXJuYXRpdmVseSB5b3UgY291bGQgcHJvYmFibHkgYXJyYW5nZSBhbmQgdGhlbiB0YWtlIGJ5IHJvdyBudW1iZXIpCgojIGlmIHlvdSBwcmVzZXJ2ZSBvciBhdmVyYWdlIHRpZXMsIHlvdSBjb3VsZCBlbmQgdXAgd2l0aCBtb3JlIG9yIGZld2VyIHRoYW4gMTAgaW4gdGhlIHJlc3VsdC4gVGhlcmVmb3JlIHRvIGdldCBleGFjdGx5IDEwIHdlIHdpbGwgYnJlYWsgdGllcyByYW5kb21seS4KCihhcnJfcmFua2VkIDwtIG11dGF0ZShmbGlnaHRzLCBhcnJfZGVsYXlfcmFuayA9IHJhbmsoZGVzYyhhcnJfZGVsYXkpLCB0aWVzLm1ldGhvZCA9ICJyYW5kb20iKSkpCih0b3BfMTBfZGVsYXkgPC0gYXJyX3JhbmtlZCAlPiUgIGZpbHRlcihhcnJfZGVsYXlfcmFuayA8PSAxMCkpCmBgYAoKIyBgc3VtbWFyaXNlKClgIGNvbGxhcHNlcyBncm91cHMgaW50byBzaW5nbGUgdmFsdWVzICAKCipXaGVuIHVzaW5nIGBzdW1tYXJpc2UoKWAgeW91IHVzdWFsbHkgYWxzbyB3YW50IGdyb3VwcyBjcmVhdGVkIGJ5IGBncm91cF9ieSgpYC4gKElmIHlvdSBkaWRuJ3QgaGF2ZSBhIGdyb3VwLCB5b3UgY291bGQganVzdCBjYWxsIHRoZSBkZXNpcmVkIGZ1bmN0aW9ucyBvbiB3aG9sZSBjb2x1bW5zLikgIAoKKllvdSBjYW4gZ3JvdXAgYnkgbXVsdGlwbGUgbGV2ZWxzICAKCipJZiB5b3UgbmVlZCB0byByZW1vdmUgYSBncm91cGluZyB1c2UgYHVuZ3JvdXAoKWAgIAoKKlVzaW5nIHRoZSBwaXBlIHdlIGNhbiBxdWlja2x5IGZlZWQgZ3JvdXBzIGludG8gc3VtbWFyaWVzIGFuZCBkbyBvdGhlciB1c2VmdWwgdGFza3MgZWZmaWNpZW50bHkuICAKCipBbW9uZyB0aWR5dmVyc2UgZnVuY3Rpb25zLCBvbmx5IGdncGxvdDIgZG9lc24ndCB3b3JrIGFzIHdlbGwgd2l0aCBwaXBlczogIml0IHdhcyB3cml0dGVuIGJlZm9yZSB0aGUgcGlwZSB3YXMgZGlzY292ZXJlZC4gVW5mb3J0dW5hdGVseSwgdGhlIG5leHQgaXRlcmF0aW9uIG9mIGdncGxvdDIsIGdndmlzLCB3aGljaCBkb2VzIHVzZSB0aGUgcGlwZSwgaXNu4oCZdCBxdWl0ZSByZWFkeSBmb3IgcHJpbWUgdGltZSB5ZXQuIiAgQnV0IGl0IGlzIHVzZWQgaW4gc29tZSBleGFtcGxlcyBiZWxvdy4gIAoKCiAgCmBgYHtyIGdyb3VwIHN1bW1hcmlzZSBhbmQgcGlwZSBleGFtcGxlcywgZWNobyA9IFRSVUV9CgojIHdpdGggcGlwZQooZGVsYXlzIDwtIGZsaWdodHMgJT4lIAogIGdyb3VwX2J5KGRlc3QpICU+JSAKICBzdW1tYXJpc2UoCiAgICBjb3VudCA9IG4oKSwKICAgIGRpc3QgPSBtZWFuKGRpc3RhbmNlLCBuYS5ybSA9IFRSVUUpLAogICAgZGVsYXkgPSBtZWFuKGFycl9kZWxheSwgbmEucm0gPSBUUlVFKQogICkgJT4lIAogIGZpbHRlcihjb3VudCA+IDIwLCBkZXN0ICE9ICJITkwiKSkKYGBgCgoqQmV3YXJlIC0tIGlmIHlvdSBoYXZlIGFueSBtaXNzaW5nIHZhbHVlcyAoTkFzKSBpbiB5b3VyIGRhdGEgeW91ciBzdW1tYXJ5IHZhbHVlIHdpbGwgYWxzbyBiZSBOQSwgdW5sZXNzIHlvdSBzZXQgYG5hLnJtID0gVFJVRWAuICAKCiogIi4uLml04oCZcyBhbHdheXMgYSBnb29kIGlkZWEgdG8gaW5jbHVkZSBlaXRoZXIgYSBjb3VudCAobigpKSwgb3IgYSBjb3VudCBvZiBub24tbWlzc2luZyB2YWx1ZXMgKHN1bSghaXMubmEoeCkpKS4gVGhhdCB3YXkgeW91IGNhbiBjaGVjayB0aGF0IHlvdeKAmXJlIG5vdCBkcmF3aW5nIGNvbmNsdXNpb25zIGJhc2VkIG9uIHZlcnkgc21hbGwgYW1vdW50cyBvZiBkYXRhLiIgIAoqKiBpbiB0aGUgZXhhbXBsZSwgbm90ZSB0aGF0ICJ3aGVuZXZlciB5b3UgcGxvdCBhIG1lYW4gKG9yIG90aGVyIHN1bW1hcnkpIHZzLiBncm91cCBzaXplLCB5b3XigJlsbCBzZWUgdGhhdCB0aGUgdmFyaWF0aW9uIGRlY3JlYXNlcyBhcyB0aGUgc2FtcGxlIHNpemUgaW5jcmVhc2VzLiIgIAoKIyMgT3RoZXIgZnVuY3Rpb25zIGZvciBzdW1tYXJpc2luZyBbc2ljXSAgCgoqIENvdW50LCBTdW0sIE1lZGlhbiwgZXRjLiAgCioqIE90aGVyIHR5cGVzIG9mIGNvdW50OiBuKCksIG5fZGlzdGluY3QoeCksIHN1bSghaXMubmEoeCkpOyBjb3VudCh4LCBzb3J0ID0gVFJVRSkgcHV0cyBsYXJnZXN0IGdyb3VwcyBhdCB0b3AKKiBNZWFzdXJlcyBvZiBzcHJlYWQ6IHNkKHgpLCBJUVIoeCksIG1hZCh4KS4gVGhlIGludGVycXVhcnRpbGUgcmFuZ2UgSVFSKHgpIGFuZCBtZWRpYW4gYWJzb2x1dGUgZGV2aWF0aW9uIG1hZCh4KSBhcmUgcm9idXN0IGVxdWl2YWxlbnRzIHRoYXQgbWF5IGJlIG1vcmUgdXNlZnVsIGlmIHlvdSBoYXZlIG91dGxpZXJzLiAgCiogU3VtbWFyaWVzIG9mIGxvZ2ljYWwgc3Vic2V0cyBzdWNoIGFzIGBtZWFuKGFycl9kZWxheVthcnJfZGVsYXkgPiAwXSlgID0gdGhlIG1lYW4gb2YgYWxsIHRob3NlIHZhbHVlcyA+IDAgIAoqIFJhbmtpbmdzOiBtaW4oeCksIHF1YW50aWxlKHgsIDAuMjUpIFsyNXRoIHBlcmNlbnRpbGUgdmFsdWVdLCBtYXgoeCkKKiBQb3NpdGlvbnM6IGZpcnN0KHgpLCBudGgoeCwgMiksIGxhc3QoeCkuIFRoZXNlIHdvcmsgc2ltaWxhcmx5IHRvIHhbMV0sIHhbMl0sIGFuZCB4W2xlbmd0aCh4KV0gYnV0IGxldCB5b3Ugc2V0IGEgZGVmYXVsdCB2YWx1ZSBpZiB0aGF0IHBvc2l0aW9uIGRvZXMgbm90IGV4aXN0IAoKYGBge3IgaGFuZGxpbmcgTkFzLCBlY2hvID0gVFJVRX0KIyBwbG90IGRlbGF5IHZzIGRpc3RhbmNlCihwIDwtIGdncGxvdChkYXRhID0gZGVsYXlzLCBtYXBwaW5nID0gYWVzKHggPSBkaXN0LCB5ID0gZGVsYXkpKSArCiAgIGdlb21fcG9pbnQoYWVzKHNpemUgPSBjb3VudCksIGFscGhhID0gMS8zKSArCiAgIGdlb21fc21vb3RoKHNlID0gRkFMU0UpCikKIyByZW1vdmUgTkFzIHdoZW4gY29tcHV0aW5nIHRoZSBzdW1tYXJ5CihkZWxheWVkIDwtIGZsaWdodHMgJT4lIAogIGdyb3VwX2J5KHllYXIsIG1vbnRoLCBkYXkpICU+JSAKICBzdW1tYXJpc2UobWVhbiA9IG1lYW4oZGVwX2RlbGF5LCBuYS5ybSA9IFRSVUUpKSkKCiMgcmVtb3ZlIE5BcyBieSByZW1vdmluZyBkYXRhIGF0IHRoZSBiZWdpbm5pbmcKKG5vdF9jYW5jZWxsZWQgPC0gZmxpZ2h0cyAlPiUgCiAgZmlsdGVyKCFpcy5uYShkZXBfZGVsYXkpLCAhaXMubmEoYXJyX2RlbGF5KSkKKQoodHJ1ZV9kZWxheWVkIDwtIG5vdF9jYW5jZWxsZWQgJT4lIAogIGdyb3VwX2J5KHllYXIsIG1vbnRoLCBkYXkpICU+JSAKICBzdW1tYXJpc2UobWVhbiA9IG1lYW4oZGVwX2RlbGF5LCBuYS5ybSA9IFRSVUUpKSkKCiMgd2hpY2ggcGxhbmVzIGhhdmUgdGhlIGdyZWF0ZXN0IGRlbGF5cz8KKGRlbGF5cyA8LSBub3RfY2FuY2VsbGVkICU+JSAKICBncm91cF9ieSh0YWlsbnVtKSAlPiUgCiAgc3VtbWFyaXNlKAogICAgZGVsYXkgPSBtZWFuKGFycl9kZWxheSkKICApKQojIGhpc3RvZ3JhbSBvZiBhdmVyYWdlIGRlbGF5IHRpbWVzCihwMSA8LSBnZ3Bsb3QoZGF0YSA9IGRlbGF5cywgbWFwcGluZyA9IGFlcyh4ID0gZGVsYXkpKSArIAogIGdlb21fZnJlcXBvbHkoYmlud2lkdGggPSAxMCkpCgojIHJlbW92ZSBOQXMgYW5kIGNhbmNlbGxlZAooZGVsYXlzIDwtIG5vdF9jYW5jZWxsZWQgJT4lIAogIGdyb3VwX2J5KHRhaWxudW0pICU+JSAKICBzdW1tYXJpc2UoCiAgICBkZWxheSA9IG1lYW4oYXJyX2RlbGF5LCBuYS5ybSA9IFRSVUUpLAogICAgbiA9IG4oKQogICkpCiMgc2NhdHRlcnBsb3Qgb2YgYXZlcmFnZSBkZWxheSB2cyBuIChyb3RhdGVkIGhpc3RvZ3JhbSBhY3R1YWxseSwgYnV0IHlvdSBjYW4gc2VlIG1vcmUgcG9pbnRzKQojIHNob3dzIHRoYXQgdGhlIGhpZ2ggYXZlcmFnZXMgYXJlIGFsbW9zdCBhbGwgYmFzZWQgb24gZmV3IGRhdGEgcG9pbnRzCihwMyA8LSBnZ3Bsb3QoZGF0YSA9IGRlbGF5cywgbWFwcGluZyA9IGFlcyh4ID0gbiwgeSA9IGRlbGF5KSkgKyAKICBnZW9tX3BvaW50KGFscGhhID0gMS8xMCkpCgojIGZpbHRlciBvdXQgdGhlIHBvaW50cyB3aXRoIGxvdyBuIGJlZm9yZSBwbG90dGluZwoocDQgPC0gZGVsYXlzICU+JSAKICBmaWx0ZXIobiA+IDI1KSAlPiUgCiAgZ2dwbG90KG1hcHBpbmcgPSBhZXMoeCA9IG4sIHkgPSBkZWxheSkpICsgCiAgICBnZW9tX3BvaW50KGFscGhhID0gMS8xMCkpCgojIGluc3RlYWQgb2YgYXZlcmFnaW5nIGVhcmx5IGFuZCBsYXRlIGZsaWdodHMgdG9nZXRoZXIsIHN0YXJ0IHdpdGggdGhlIGxvZ2ljYWwgc3Vic2V0IG9mIHRoZSBwb3NpdGl2ZSAobGF0ZSkgZGVsYXlzCihub3RfY2FuY2VsbGVkICU+JSAKICBncm91cF9ieSh5ZWFyLCBtb250aCwgZGF5KSAlPiUgCiAgc3VtbWFyaXNlKAogICAgYXZnX2RlbGF5MSA9IG1lYW4oYXJyX2RlbGF5KSwKICAgIGF2Z19kZWxheTIgPSBtZWFuKGFycl9kZWxheVthcnJfZGVsYXkgPiAwXSkgIyB0aGUgYXZlcmFnZSBwb3NpdGl2ZSBkZWxheQogICkpCgojIGxvb2tpbmcgYXQgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIGRpc3RhbmNlIHRvIGVhY2ggZGVzdGluYXRpb24KKHNwcmVhZF9vZl9vcmlnaW5zIDwtIG5vdF9jYW5jZWxsZWQgJT4lIAogIGdyb3VwX2J5KGRlc3QpICU+JSAKICBzdW1tYXJpc2UoZGlzdGFuY2Vfc2QgPSBzZChkaXN0YW5jZSkpICU+JSAKICBhcnJhbmdlKGRlc2MoZGlzdGFuY2Vfc2QpKSkKCiMgV2hlbiBkbyB0aGUgZmlyc3QgYW5kIGxhc3QgZmxpZ2h0cyBsZWF2ZSBlYWNoIGRheT8KKGZpcnN0X2FuZF9sYXN0IDwtIG5vdF9jYW5jZWxsZWQgJT4lIAogIGdyb3VwX2J5KHllYXIsIG1vbnRoLCBkYXkpICU+JSAKICBzdW1tYXJpc2UoCiAgICBmaXJzdCA9IG1pbihkZXBfdGltZSksCiAgICBsYXN0ID0gbWF4KGRlcF90aW1lKQogICkpCgooZmlyc3RfbGFzdF8yIDwtIG5vdF9jYW5jZWxsZWQgJT4lIAogIGdyb3VwX2J5KHllYXIsIG1vbnRoLCBkYXkpICU+JSAKICBzdW1tYXJpc2UoCiAgICBmaXJzdF9kZXAgPSBmaXJzdChkZXBfdGltZSksICMgVGhpcyB3b3JrcyBPTkxZIGJlY2F1c2UgdGhlIHRhYmxlIGlzIGFscmVhZHkgb3JkZXJlZCBieSBkZXBfdGltZSEKICAgIGxhc3RfZGVwID0gbGFzdChkZXBfdGltZSkKICApKQoKIyBhbm90aGVyIHdheSB0byBmaW5kIHRoZSAxc3QgYW5kIGxhc3QgcGVyIGRheQoocmFua2ZpbHQgPC0gbm90X2NhbmNlbGxlZCAlPiUgCiAgZ3JvdXBfYnkoeWVhciwgbW9udGgsIGRheSkgJT4lIAogIG11dGF0ZShyID0gbWluX3JhbmsoZGVzYyhkZXBfdGltZSkpKSAlPiUgCiAgZmlsdGVyKHIgJWluJSByYW5nZShyKSkpICMgcmFuZ2UgZ2l2ZXMgeW91IHRoZSBtaW4gYW5kIG1heCB2YWx1ZXMKYGBgCgpZb3UgY2FuIGdyb3VwIGhpZXJhcmNoaWNhbGx5IGFuZCBzZWUgc3VtbWFyaWVzIHdpdGhpbiBzdW1tYXJpZXMuIFlvdSBjYW4gdW5ncm91cCBpZiBuZWVkZWQuICAKCmBgYHtyIG1vcmUgZ3JvdXAgYW5kIHVuZ3JvdXAgZnVuY3Rpb25zLCBlY2hvID0gVFJVRX0KIyBncm91cCBtdWx0aXBsZSBsZXZlbHMKCmRhaWx5IDwtIGdyb3VwX2J5KGZsaWdodHMsIHllYXIsIG1vbnRoLCBkYXkpCihwZXJfZGF5ICAgPC0gc3VtbWFyaXNlKGRhaWx5LCBmbGlnaHRzID0gbigpKSkKCiMgdW5ncm91cAoKKGRhaWx5ICU+JSAKICB1bmdyb3VwKCkgJT4lICAgICAgICAgICAgICMgbm8gbG9uZ2VyIGdyb3VwZWQgYnkgZGF0ZQogIHN1bW1hcmlzZShmbGlnaHRzID0gbigpKSkgICMgYWxsIGZsaWdodHMKCmBgYAoKCgpIZXJlIHdlIGNvbnRpbnVlIGRpZ3Jlc3NpbmcgKElNTykgb24gdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHZhcmlhdGlvbiBhbmQgc2FtcGxlIHNpemUsIHVzaW5nIGV4YW1wbGVzIGZyb20gdGhlIExhaG1hbiBiYXNlYmFsbCBzdGF0aXN0aWNzIHBhY2thZ2UuCgpgYGB7ciBhZ2dyZWdhdGUgdmFyaWF0aW9uIGV4YW1wbGVzLCBlY2hvID0gVFJVRX0KIyBDb252ZXJ0IHRvIGEgdGliYmxlIHNvIGl0IHByaW50cyBuaWNlbHkKKGJhdHRpbmcgPC0gYXNfdGliYmxlKExhaG1hbjo6QmF0dGluZykpCgojIGJhdHRpbmcgYXZlcmFnZSB2cyBhdCBiYXRzIHBlciBwbGF5ZXIKCgojIHZpc3VhbGl6ZSBhdmcgd2l0aCB0aGUgbnVtYmVyIG9mIGF0LWJhdHMgCihiYXR0ZXJzIDwtIGJhdHRpbmcgJT4lIAogIGdyb3VwX2J5KHBsYXllcklEKSAlPiUgCiAgc3VtbWFyaXNlKAogICAgYmEgPSBzdW0oSCwgbmEucm0gPSBUUlVFKSAvIHN1bShBQiwgbmEucm0gPSBUUlVFKSwKICAgIGFiID0gc3VtKEFCLCBuYS5ybSA9IFRSVUUpCiAgKSkKCihwIDwtIGJhdHRlcnMgJT4lIAogIGZpbHRlcihhYiA+IDEwMCkgJT4lIAogIGdncGxvdChtYXBwaW5nID0gYWVzKHggPSBhYiwgeSA9IGJhKSkgKwogICAgZ2VvbV9wb2ludCgpICsgCiAgICBnZW9tX3Ntb290aChzZSA9IEZBTFNFKSkKCiMgd2hvIGFyZSB0aGUgImJlc3QiIGJhdHRlcnM/IFRoZSBsdWNreSBvbmVzPyBOb3RlIHRoZSBhdC1iYXRzIGZvciB0aGUgdG9wIG9mIHRoZSBsaXN0CihoaWdoX2F2ZyA8LSBiYXR0ZXJzICU+JSAKICBhcnJhbmdlKGRlc2MoYmEpKQopCmBgYApOb3cgd2UgZ2V0IGJhY2sgdG8gdGhlIGZsaWdodCBkYXRhIGFuZCB0cnkgbWV0aG9kcyBvZiBjb3VudGluZy4gIAoKKiBDb3VudCB0aGUgY2FycmllcnMgZ29pbmcgdG8gZWFjaCBkZXN0aW5hdGlvbiAgCiogQ291bnQgdmFsaWQgYW5kIGludmFsaWQgdmFsdWVzIHVzaW5nIGlzLm5hICAKKiBDb3VudCB2YWx1ZXMgd2l0aGluIGNlcnRhaW4gcmFuZ2VzICAKKiBXZSBjYW4gKndlaWdodCogb3VyIGNvdW50cyBieSBhbm90aGVyIGNvbHVtbiwgZS5nLiB3aGljaCBhaXJjcmFmdCBoYXZlIHRoZSBtb3N0IGZsaWdodCBtaWxlcy4gIAoqIEJVVCBiZSBjYXJlZnVsIHdoZW4gc3VtbWFyaXNpbmcgYnkgbXVsdGlwbGUgbGV2ZWxzLiBUaGUgbWVhbiBvZiAyIGRpZmZlcmVudC1zaXplZCBncm91cHMgaXMgTk9UIHRoZSBtZWFuIG9mIHRoZSAyIG1lYW5zLiBUaGVuLCB3ZWlnaHRpbmcgaXMgdXNlZnVsLiAKCgpgYGB7ciBjb3VudCBleGFtcGxlcywgZWNobyA9IFRSVUV9CiMgV2hpY2ggZGVzdGluYXRpb25zIGhhdmUgdGhlIG1vc3QgY2FycmllcnM/Cihub3RfY2FuY2VsbGVkICU+JSAKICBncm91cF9ieShkZXN0KSAlPiUgCiAgc3VtbWFyaXNlKGNhcnJpZXJzID0gbl9kaXN0aW5jdChjYXJyaWVyKSkgJT4lICMgdW5pcXVlIHZhbHVlcwogIGFycmFuZ2UoZGVzYyhjYXJyaWVycykpKQoKIyBob3cgbWFueSB2YWx1ZXMgYXJlIGVudGVyZWQ/Cih2YWxpZF9hcnJfdGltZSA8LSBzdW0oIWlzLm5hKG5vdF9jYW5jZWxsZWQkYXJyX3RpbWUpKSkKIyBvciB0byBnZXQgdGhlIGZyYWN0aW9uIG9mIHZhbGlkIHZhbHVlcwoodmFsaWRfYXJyX3RpbWVfZnJhYyA8LSBtZWFuKCFpcy5uYShub3RfY2FuY2VsbGVkJGFycl90aW1lKSkpCih2YWxpZF9hcnJfdGltZV9mcmFjMiA8LSBtZWFuKCFpcy5uYShmbGlnaHRzJGFycl90aW1lKSkpCgojIGNvdW50KCkgaXMgYSBkcGx5ciBzdW1tYXJ5IG9mIGEgZ3JvdXBlZCB0YWJsZQoKKGZsaWdodHNfcGVyX2Rlc3QgPC0gbm90X2NhbmNlbGxlZCAlPiUgCiAgY291bnQoZGVzdCkpCgojIHlvdSBjYW4gd2VpZ2h0IGNvdW50cyBieSBhbm90aGVyIHZhcmlhYmxlCgooYWlyY3JhZnRfbWlsZXMgPC0gbm90X2NhbmNlbGxlZCAlPiUgCiAgY291bnQodGFpbG51bSwgd3QgPSBkaXN0YW5jZSkpCgoKYGBgCgoKIyMgRXhlcmNpc2VzIGZvciBjb3VudGluZyBhbmQgc3VtbWFyaXNpbmcKCjEuIEJyYWluc3Rvcm0gYXQgbGVhc3QgNSBkaWZmZXJlbnQgd2F5cyB0byBhc3Nlc3MgdGhlIHR5cGljYWwgZGVsYXkgY2hhcmFjdGVyaXN0aWNzIG9mIGEgZ3JvdXAgb2YgZmxpZ2h0cy4gQ29uc2lkZXIgdGhlIGZvbGxvd2luZyBzY2VuYXJpb3M6ICAKCiogQSBmbGlnaHQgaXMgMTUgbWludXRlcyBlYXJseSA1MCUgb2YgdGhlIHRpbWUsIGFuZCAxNSBtaW51dGVzIGxhdGUgNTAlIG9mIHRoZSB0aW1lLiAgCiogQSBmbGlnaHQgaXMgYWx3YXlzIDEwIG1pbnV0ZXMgbGF0ZS4gIAoqIEEgZmxpZ2h0IGlzIDMwIG1pbnV0ZXMgZWFybHkgNTAlIG9mIHRoZSB0aW1lLCBhbmQgMzAgbWludXRlcyBsYXRlIDUwJSBvZiB0aGUgdGltZS4gIAoqIDk5JSBvZiB0aGUgdGltZSBhIGZsaWdodCBpcyBvbiB0aW1lLiAxJSBvZiB0aGUgdGltZSBpdOKAmXMgMiBob3VycyBsYXRlLiAgCgoqVG8gY2xhcmlmeSB0aGVzZSBkaWZmZXJlbmNlcywgdXNlICUgb2YgZmxpZ2h0cyB0aGF0IGFyZSBkZWxheWVkOyBtZWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gb2YgZGVsYXlzIGluY2x1ZGluZyBwb3MgYW5kIG5lZzsgbWVhbiBhYnNvbHV0ZSB2YWx1ZSBvZiBkZWxheXMgPiAwOyBtZWRpYW4gZGVsYXkqICAKCldoaWNoIGlzIG1vcmUgaW1wb3J0YW50OiBhcnJpdmFsIGRlbGF5IG9yIGRlcGFydHVyZSBkZWxheT8gIAoKKlRvIHRoZSBwYXNzZW5nZXIsIGFycml2YWwgZGVsYXkgaXMgbW9zdCBpbXBvcnRhbnQuIFRvIHRoZSBhaXJwb3J0LCBkZXBhcnR1cmUgZGVsYXkgaXMgYWxzbyBpbXBvcnRhbnQgYmVjYXVzZSBvdGhlciBmbGlnaHRzIGFyZSBhZmZlY3RlZC4qICAKCjIuIENvbWUgdXAgd2l0aCBhbm90aGVyIGFwcHJvYWNoIHRoYXQgd2lsbCBnaXZlIHlvdSB0aGUgc2FtZSBvdXRwdXQgYXMgbm90X2NhbmNlbGxlZCAlPiUgY291bnQoZGVzdCkgYW5kIG5vdF9jYW5jZWxsZWQgJT4lIGNvdW50KHRhaWxudW0sIHd0ID0gZGlzdGFuY2UpICh3aXRob3V0IHVzaW5nIGNvdW50KCkpLiAgCgoqIGNvdW50IHRoZSBmbGlnaHRzIHBlciBkZXN0aW5hdGlvbiB3aXRob3V0IGBjb3VudCgpYDogIHVzZSBgbigpYCAgCiogZ2V0IGFpcmNyYWZ0IG1pbGVzOiBncm91cCBieSB0aGUgdGFpbG51bWJlciBhbmQgc3VtIHRoZSBtaWxlcyAgCgozLiBPdXIgZGVmaW5pdGlvbiBvZiBjYW5jZWxsZWQgZmxpZ2h0cyAoIGAoaXMubmEoZGVwX2RlbGF5KSB8IGlzLm5hKGFycl9kZWxheSlgICkgaXMgc2xpZ2h0bHkgc3Vib3B0aW1hbC4gV2h5PyBXaGljaCBpcyB0aGUgbW9zdCBpbXBvcnRhbnQgY29sdW1uPyAgCgoqIE9uZSBvZiB0aGVzZSBjb2x1bW5zIHNob3VsZCBiZSBzdWZmaWNpZW50IHRvIGNoZWNrLiBUaGUgYXJyaXZhbCBtaWdodCBiZSB0aGUgbW9zdCBpbXBvcnRhbnQgc2luY2Ugb2NjYXNpb25hbGx5IGEgZmxpZ2h0IGNvdWxkICJkZXBhcnQiIHRoZSBnYXRlIHRoZW4gcmV0dXJuIGZyb20gdGhlIHRhcm1hYy4gT3IgdGhlcmUgY291bGQgYmUgbWlzc2luZyBkYXRhLiBNb3JlIHN0cmluZ2VudGx5IHlvdSBjb3VsZCBjaGVjayBmb3IgdGhlIGludGVyc2VjdGlvbiAoYCZgKSAgCgo0LiBMb29rIGF0IHRoZSBudW1iZXIgb2YgY2FuY2VsbGVkIGZsaWdodHMgcGVyIGRheS4gSXMgdGhlcmUgYSBwYXR0ZXJuPyBJcyB0aGUgcHJvcG9ydGlvbiBvZiBjYW5jZWxsZWQgZmxpZ2h0cyByZWxhdGVkIHRvIHRoZSBhdmVyYWdlIGRlbGF5PyAgCgoqIGdyb3VwYnkgeWVhciBtb250aCBkYXksIHN1bW1hcml6ZSBudW0gY2FuY2VsbGVkIGFuZCBhdmVyYWdlIGRlbGF5LCBncmFwaCAgCgo1LiBXaGljaCBjYXJyaWVyIGhhcyB0aGUgd29yc3QgZGVsYXlzPyBDaGFsbGVuZ2U6IGNhbiB5b3UgZGlzZW50YW5nbGUgdGhlIGVmZmVjdHMgb2YgYmFkIGFpcnBvcnRzIHZzLiBiYWQgY2FycmllcnM/IFdoeS93aHkgbm90PyAoSGludDogdGhpbmsgYWJvdXQgZmxpZ2h0cyAlPiUgZ3JvdXBfYnkoY2FycmllciwgZGVzdCkgJT4lIHN1bW1hcmlzZShuKCkpKSAgCgoqR3JvdXAgYnkgY2FycmllciBBTkQgYWlycG9ydCwgbG9vayBhdCAlIG9uLXRpbWUgZmxpZ2h0cywgbWVhbiBhbmQgbWVkaWFuIGRlbGF5ICg+MCBhbmQgdG90YWwpLiBDb21wYXJlIGNhcnJpZXIgaW4gYW4gYWlycG9ydCB0byB0b3RhbCBmb3IgdGhhdCBhaXJwb3J0LCBvciAoaW4gY2FzZSBvbmUgcHJlZG9taW5hdGVzLCBjaGVjayAlIGZsaWdodHMgZnJvbSB0aGF0IGNhcnJpZXIpIHRoZSB0b3RhbCBmb3Igb3RoZXIgY2FycmllcnMgYXQgdGhlIHNhbWUgYWlycG9ydC4qICAKCjYuIFdoYXQgZG9lcyB0aGUgc29ydCBhcmd1bWVudCB0byBjb3VudCgpIGRvPyBXaGVuIG1pZ2h0IHlvdSB1c2UgaXQ/ICAKCipJdCBkaXNwbGF5cyB0aGUgbGFyZ2VzdCBncm91cHMgYXQgdGhlIHRvcCBvZiB0aGUgb3V0cHV0LiBZb3UgY291bGQgdXNlIGl0IHRvIGRvIGEgcXVpY2tpZSBzdW1tYXJ5IGluc3RlYWQgb2YgMiBzZXBhcmF0ZSBjb21tYW5kcyB0byBmaW5kLCBlLmcuLCB0aGUgbW9zdCBkZWxheWVkIGZsaWdodHMgb3Igd2hhdGV2ZXIuKgoKYGBge3Igc3VtbWFyeSBleGVyY2lzZXMsIGVjaG8gPSBUUlVFfQojIC0tLS0gYXNzZXNzIGxhdGVuZXNzIGluIGRpZmZlcmVudCB3YXlzCiMgZGlzdGluZ3Vpc2ggZWFybHkgKyBsYXRlIHZzIGNvbnN0YW50bHkgbGF0ZSB2cy4gbmVhcmx5IGFsd2F5cyBvbiB0aW1lOyAKI21lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBvZiBkZWxheXM7IG1lYW4gYWJzb2x1dGUgdmFsdWUgb2YgZGVsYXlzOyBtZWRpYW4gZGVsYXkKCgojIGNvdW50IHRoZSBmbGlnaHRzIHBlciBkZXN0aW5hdGlvbiB3aXRob3V0IGNvdW50KCkKKGZsaWdodHMgJT4lIGdyb3VwX2J5KGRlc3QpICU+JSBzdW1tYXJpc2UobigpKSkgCgojIGdldCBhaXJjcmFmdCBtaWxlczogZ3JvdXAgYnkgdGhlIHRhaWxudW1iZXIgYW5kIHN1bSB0aGUgbWlsZXMgIAoKYGBgCgojIENvbWJpbmluZyBmdW5jdGlvbnM6IEdyb3VwZWQgbXV0YXRlcyBhbmQgZmlsdGVycyAgCgpgYGB7ciBncm91cGVkIGZ1bmN0aW9ucyBleGFtcGxlcywgZWNobyA9IFRSVUV9CiNGaW5kIHRoZSB3b3JzdCBtZW1iZXJzIG9mIGVhY2ggZ3JvdXA6CgpmbGlnaHRzX3NtbCAlPiUgCiAgZ3JvdXBfYnkoeWVhciwgbW9udGgsIGRheSkgJT4lCiAgZmlsdGVyKHJhbmsoZGVzYyhhcnJfZGVsYXkpKSA8IDEwKQoKIyBGaW5kIGFsbCBncm91cHMgYmlnZ2VyIHRoYW4gYSB0aHJlc2hvbGQ6Cgpwb3B1bGFyX2Rlc3RzIDwtIGZsaWdodHMgJT4lIAogIGdyb3VwX2J5KGRlc3QpICU+JSAKICBmaWx0ZXIobigpID4gMzY1KQoKIyBTdGFuZGFyZGlzZSB0byBjb21wdXRlIHBlciBncm91cCBtZXRyaWNzOgoKcG9wdWxhcl9kZXN0cyAlPiUgCiAgZmlsdGVyKGFycl9kZWxheSA+IDApICU+JSAKICBtdXRhdGUocHJvcF9kZWxheSA9IGFycl9kZWxheSAvIHN1bShhcnJfZGVsYXkpKSAlPiUgCiAgc2VsZWN0KHllYXI6ZGF5LCBkZXN0LCBhcnJfZGVsYXksIHByb3BfZGVsYXkpCgoKYGBgCgojIyBFeGVyY2lzZXMgZm9yIGdyb3VwZWQgbXV0YXRlcwoKMS4gUmVmZXIgYmFjayB0byB0aGUgbGlzdHMgb2YgdXNlZnVsIG11dGF0ZSBhbmQgZmlsdGVyaW5nIGZ1bmN0aW9ucy4gRGVzY3JpYmUgaG93IGVhY2ggb3BlcmF0aW9uIGNoYW5nZXMgd2hlbiB5b3UgY29tYmluZSBpdCB3aXRoIGdyb3VwaW5nLiAgCgoyLiBXaGljaCBwbGFuZSAodGFpbG51bSkgaGFzIHRoZSB3b3JzdCBvbi10aW1lIHJlY29yZD8gIAoKKmNoZWNrICUgb24tdGltZSoKKmNoZWNrICUgb24tdGltZSB3aXRoaW4gc29tZSBpbnRlcnZhbCoKCjMuIFdoYXQgdGltZSBvZiBkYXkgc2hvdWxkIHlvdSBmbHkgaWYgeW91IHdhbnQgdG8gYXZvaWQgZGVsYXlzIGFzIG11Y2ggYXMgcG9zc2libGU/ICAKCipkaXZpZGUgZGF5IGludG8gYmlucyBhbmQgc3VtbWFyaXNlLCBvciBwbG90IHRpbWUgdnMgJSBvbi10aW1lKgoKNC4gRm9yIGVhY2ggZGVzdGluYXRpb24sIGNvbXB1dGUgdGhlIHRvdGFsIG1pbnV0ZXMgb2YgZGVsYXkuIEZvciBlYWNoIGZsaWdodCwgY29tcHV0ZSB0aGUgcHJvcG9ydGlvbiBvZiB0aGUgdG90YWwgZGVsYXkgZm9yIGl0cyBkZXN0aW5hdGlvbi4gIAoKKmdyb3VwIGJ5IGRlc3QsIHN1bSBkZWxheXMgKHBvc2l0aXZlKS4gMm5kIHF1ZXN0aW9uIGlzIG5vdCB2ZXJ5IGNsZWFyIC0tIGJ1dCBpIHRoaW5rIHRoZXkgd2FudCAoZGVsYXkgZm9yIGEgc2luZ2xlIGZsaWdodCkvKHRvdGFsIGRlbGF5cyBmb3IgdGhlIGRlc3RpbmF0aW9uKS4gb3IgKGRlbGF5IGZvciBhbGwgZmxpZ2h0cyB3aXRoIHRoYXQgbnVtYmVyKS8odG90YWwgZGVsYXlzIGZvciB0aGUgZGVzdGluYXRpb24pLioKCjUuIERlbGF5cyBhcmUgdHlwaWNhbGx5IHRlbXBvcmFsbHkgY29ycmVsYXRlZDogZXZlbiBvbmNlIHRoZSBwcm9ibGVtIHRoYXQgY2F1c2VkIHRoZSBpbml0aWFsIGRlbGF5IGhhcyBiZWVuIHJlc29sdmVkLCBsYXRlciBmbGlnaHRzIGFyZSBkZWxheWVkIHRvIGFsbG93IGVhcmxpZXIgZmxpZ2h0cyB0byBsZWF2ZS4gVXNpbmcgbGFnKCksIGV4cGxvcmUgaG93IHRoZSBkZWxheSBvZiBhIGZsaWdodCBpcyByZWxhdGVkIHRvIHRoZSBkZWxheSBvZiB0aGUgaW1tZWRpYXRlbHkgcHJlY2VkaW5nIGZsaWdodC4KCipzb3J0IGJ5IHRpbWUsIGdyYXBoIGRlbGF5IHZzIGRlbGF5KGxhZykqCgoKCjYuIExvb2sgYXQgZWFjaCBkZXN0aW5hdGlvbi4gQ2FuIHlvdSBmaW5kIGZsaWdodHMgdGhhdCBhcmUgc3VzcGljaW91c2x5IGZhc3Q/IChpLmUuIGZsaWdodHMgdGhhdCByZXByZXNlbnQgYSBwb3RlbnRpYWwgZGF0YSBlbnRyeSBlcnJvcikuIENvbXB1dGUgdGhlIGFpciB0aW1lIG9mIGEgZmxpZ2h0IHJlbGF0aXZlIHRvIHRoZSBzaG9ydGVzdCBmbGlnaHQgdG8gdGhhdCBkZXN0aW5hdGlvbi4gV2hpY2ggZmxpZ2h0cyB3ZXJlIG1vc3QgZGVsYXllZCBpbiB0aGUgYWlyPyAgCgo3LiBGaW5kIGFsbCBkZXN0aW5hdGlvbnMgdGhhdCBhcmUgZmxvd24gYnkgYXQgbGVhc3QgdHdvIGNhcnJpZXJzLiBVc2UgdGhhdCBpbmZvcm1hdGlvbiB0byByYW5rIHRoZSBjYXJyaWVycy4gIAoKKm1vc3Qgb250aW1lLCBsb3dlc3QgYXZlcmFnZSBkZWxheSBtYWduaXR1ZGUqCgo4LiBGb3IgZWFjaCBwbGFuZSwgY291bnQgdGhlIG51bWJlciBvZiBmbGlnaHRzIGJlZm9yZSB0aGUgZmlyc3QgZGVsYXkgb2YgZ3JlYXRlciB0aGFuIDEgaG91ci4gIAoKKmdyb3VwIGJ5IHRhaWxudW1iZXIsIHNvcnQgYnkgZGF0ZSg/KSwgZmluZCBlYXJsaWVzdCAxIGhyIGRlbGF5LCBjb3VudCBmbGlnaHRzIGJ5IHRoYXQgcGxhbmUgd2l0aCBkYXRlL3RpbWUgPCB0aGF0IHZhbHVlKgoKYGBge3IgZ3JvdXBlZCBmdW5jdGlvbnMgZXhlcmNpc2VzLCBlY2hvID0gVFJVRX0KCgpgYGA=